<?php

class Fx_Layout extends Fx_Block {

    protected $_id = '';

    protected $_baseDir = 'layouts/';

    protected $_module = '';

    protected $_file = 'layout.xml';

    protected $_pages = array();

    protected $_current_page = '';

    public function __construct() {
		parent::__construct();
        $this->_id = 'layout_id_' . md5(time());
    }

    public function getId() {
        return (string)$this->_id;
    }

    public function addPage(Fx_Page $new_page) {
        if (!empty($new_page)) {
            $new_page->setLayout($this);
            $this->_pages[$new_page->getUrl()] = $new_page;
        }
        return $this;
    }

    public function removePage(Fx_Page $new_page) {
        if (isset($this->_pages[$url])) {
            unset($this->_pages[$url]);
        }
        return $this;
    }

    public function getPage($url, $useCache = false) {
		// Cacher
        if (isset($this->_pages[$url])) {
			if (!$useCache) {
				return $this->_pages[$url];
			}
			$page = $this->_pages[$url];
			$reg = st_registry::getInstance();
			if ($reg->registry('page-'.$url) === null) {
				$reg->register('page-'.$url, $page, true);
				return $page;
			} else {
				return $reg->registry('page-'.$url);
			}
        }
        return $this;
    }
	
	// Load Relative Path: [./application/themes/default/layouts/] => Do not need
	// Load: welcome/layout.xml
    public function loadLayout($_layout = '') {
        $_layout = sprintf(APP_LAYOUT_PATH, $this->_theme) . $_layout;
		$this->setLayout($_layout);
        $this->_parseLayout();
        return $this;
    }

    public function _parseLayout() {
        $_layout = $this->getLayout();
        if (empty($_layout)) {
            return $this;
        }
        $_layouts = simplexml_load_file($_layout, 'SimpleXMLElement', LIBXML_NOBLANKS);
        // Check Loaded XML Layout Succsessfully
        if (!$_layouts instanceof SimpleXMLElement || count($_layouts->children()) == 0) {
            return $this;
        }
        // Get One By One URL Then Create Fx_Page For It
        foreach ($_layouts->children() as $_url => $_doc) {
            if (count($_doc->children()) == 0) {
                continue;
            }
            $this->addPage(new Fx_Page($_url, NULL, NULL, NULL));
            foreach ($_doc->children() as $_tagName => $_xmlBlock) {
                $_block = new Fx_Block();
                switch ($_tagName) {
                    case 'action':
                        if (isset($_xmlBlock['method'])) {
                            $method = (string)$_xmlBlock['method'];
                            $param  = preg_replace_callback("/{{(.*)}}/",
                                create_function(
                                '$matches',
                                'return constant($matches[1]);'
                                ), (string)$_xmlBlock);
                            $this->_pages[$_url]->$method($param);
                        }
                        break;
                    case 'block':
                        $_attributes = $_xmlBlock->attributes();
                        if (count($_attributes) > 0) {
                            foreach ($_attributes as $key => $value) {
                                $value = preg_replace_callback("/{{(.*)}}/",
                                    create_function(
                                    '$matches',
                                    'return constant($matches[1]);'
                                    ), $value);
                                $method = 'set' . ucfirst($key);
                                switch ($key) {
                                    case 'type':
                                        $class_name = substr($value, strrpos($value, '/') + 1);
                                        if (strstr($class_name, '_')) {
                                            $class_name = implode('_', array_map("ucfirst", explode('_', $class_name)));
                                        }
                                        $value = sprintf("%s%s%s", APP_MODULE_PATH, $value, EXT);
                                        require_once $value;
                                        if (!class_exists($class_name)) {
                                            $error_msg = "$classname not found in path: $value";
                                            throw new Exception($error_msg);
                                        }
                                        $_block = new $class_name();
                                        break;
                                    /**id, alias, name, template*/
                                    default:
                                        $_block->$method($value);
                                        break;
                                }
                            }
                        }
                        break;
                    /**Parse Params In Page*/
                    case 'params':
                        $_params = $_xmlBlock->param;
                        if (count($_params) > 0) {
                            foreach ($_params as $_param) {
                            // Param has not name then ignore it
                                if (!isset($_param['name'])) {
                                    continue;
                                }
                                $this->_pages[$_url]->addVariable($_param['name'], (string)$_param);
                            }
                        }
                        break;
                    /**Parse Constants In Page*/
                    case 'constants':
                        $_constants = $_xmlBlock->constant;
                        if (count($_constants) > 0) {
                            foreach ($_constants as $_constant) {
                            // Param has not name then ignore it
                                if (!isset($_constant['name'])) {
                                    continue;
                                }
                                if (!defined($_constant['name'])) {
                                    define($_constant['name'], (string)$_constant);
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
                if ($_block->getId()) {
                    if (!$_block->hasTemplate()) {
                        $_block->setTemplate(APP_TEMPLATE_DEFAULT);
                    }
                    $this->_pages[$_url]->appendChild($_block);
                }

                $_children = $_xmlBlock->children();
                if (count($_children) > 0) {
                    foreach ($_children as $_xmlChildBlock) {
                        $this->_recursiveXMLBlocks($_xmlChildBlock, $_block);
                    }
                }
            }
        }
        return $this;
    }

    public function _recursiveXMLBlocks(SimpleXMLElement & $_xmlBlock = NULL, & $_parentBlock = NULL) {
        if (empty($_xmlBlock) || empty($_parentBlock) || !$_parentBlock instanceof Fx_Block) {
            return $this;
        }
        $_xmlChildren = $_xmlBlock->children();
        $_child_block = $this->addXMLBlocks($_xmlBlock, $_parentBlock);
        if (count($_xmlChildren) > 0) {
            foreach ($_xmlChildren as $_xmlChildBlock) {
                $this->_recursiveXMLBlocks($_xmlChildBlock, $_child_block);
            }
        }
        return $this;
    }

    public function addXMLBlocks(SimpleXMLElement $_xmlBlock = NULL, & $_parentBlock = NULL) {
        if (empty($_xmlBlock) || empty($_parentBlock) || !$_parentBlock instanceof Fx_Block) {
            return NULL;
        }
        $_child_block = new Fx_Block();
        $_tagName     = $_xmlBlock->getName();
        switch ($_tagName) {
            case 'action':
                if (isset($_xmlBlock['method'])) {
                    $method = (string)$_xmlBlock['method'];
                    $param  = preg_replace_callback("/{{(.*)}}/",
                        create_function(
                        '$matches',
                        'return constant($matches[1]);'
                        ), (string)$_xmlBlock);
                    $_parentBlock->$method($param);
                }
                break;
            case 'block':
                $_attributes = $_xmlBlock->attributes();
                if (count($_attributes) > 0) {
                    foreach ($_attributes as $key => $value) {
                        $value = preg_replace_callback("/{{(.*)}}/",
                            create_function(
                            '$matches',
                            'return constant($matches[1]);'
                            ), $value);
                        $method = 'set' . ucfirst($key);
                        switch ($key) {
                            case 'type':
                                $class_name = substr($value, strrpos($value, '/') + 1);
                                if (strstr($class_name, '_')) {
                                    $class_name = implode('_', array_map("ucfirst", explode('_', $class_name)));
                                }
                                $value = sprintf("%s%s%s", APP_MODULE_PATH, $value, EXT);
                                require_once $value;
                                if (!class_exists($class_name)) {
                                    $error_msg = "$classname not found in path: $value";
                                    throw new Exception($error_msg);
                                }
                                $_child_block = new $class_name();
                                break;
                                /**id, alias, name, template*/
                            default:
                                $_child_block->$method($value);
                                break;
                        }
                    }
                }
                break;
            /**Parse Params In Block*/
            case 'params':
                $_params = $_xmlBlock->param;
                if (count($_params) > 0) {
                    foreach ($_params as $_param) {
                    // Param has not name then ignore it
                        if (!isset($_param['name'])) {
                            continue;
                        }
                        $_parentBlock->addVariable($_param['name'], (string)$_param);
                    }
                }
                break;
            /**Parse Constants In Block*/
            case 'constants':
                $_constants = $_xmlBlock->constant;
                if (count($_constants) > 0) {
                    foreach ($_constants as $_constant) {
                    // Param has not name then ignore it
                        if (!isset($_constant['name'])) {
                            continue;
                        }
                        if (!defined($_constant['name'])) {
                            define($_constant['name'], (string)$_constant);
                        }
                    }
                }
                break;
            default:
                break;
        }
        if ($_child_block->getId()) {
            if (!$_child_block->hasTemplate()) {
                $_child_block->setTemplate(APP_TEMPLATE_DEFAULT);
            }
            $_parentBlock->appendChild($_child_block);
            return $_child_block;
        }
        return NULL;
    }

    public function getPageHtml($_url) {
        if (isset($this->_pages[$_url])) {
            return $this->pages[$_url]->getOutputHtml();
        }
        return NULL;
    }

    public function getLayout() {
        return (string)$this->_layout;
    }

    public function setLayout($_layout) {
        if (!isset($_layout) || !is_string($_layout)) {
            return $this;
        }
        if (!file_exists($_layout)) {
            throw new Exception("Layout File: $_layout Not Found");
        }
        $this->_layout = $_layout;
        return $this;
    }
	
	public function hasPage($_pageIndex) {
		if (isset($this->_pages[$_pageIndex])) {
            return true;
        }	
		return false;
	}
}
?>